package com.feicheng.service.impl;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.feicheng.common.constant.Constant;
import com.feicheng.common.exception.SystemException;
import com.feicheng.common.result.ResultInfo;
import com.feicheng.common.wx.WxHTTPInterface;
import com.feicheng.common.wx.pay.WxPayBean;
import com.feicheng.persistence.entity.Account;
import com.feicheng.persistence.entity.CardRecord;
import com.feicheng.persistence.entity.CardType;
import com.feicheng.persistence.entity.ClientDeposit;
import com.feicheng.persistence.entity.ClientVip;
import com.feicheng.persistence.entity.ConsumeRecord;
import com.feicheng.persistence.entity.DepositBackRequest;
import com.feicheng.persistence.entity.DepositRecord;
import com.feicheng.persistence.entity.Order;
import com.feicheng.persistence.entity.OrderConsume;
import com.feicheng.persistence.entity.PaymentRecord;
import com.feicheng.persistence.entity.StoreClient;
import com.feicheng.persistence.entity.User;
import com.feicheng.persistence.entity.Vip;
import com.feicheng.persistence.service.IAccountService;
import com.feicheng.persistence.service.ICardRecordService;
import com.feicheng.persistence.service.ICardTypeService;
import com.feicheng.persistence.service.IClientDepositService;
import com.feicheng.persistence.service.IClientVipService;
import com.feicheng.persistence.service.IConsumeRecordService;
import com.feicheng.persistence.service.IDepositBackRequestService;
import com.feicheng.persistence.service.IDepositRecordService;
import com.feicheng.persistence.service.IOrderConsumeService;
import com.feicheng.persistence.service.IOrderService;
import com.feicheng.persistence.service.IPaymentRecordService;
import com.feicheng.persistence.service.IStoreClientService;
import com.feicheng.persistence.service.IVipService;
import com.feicheng.service.BaseService;
import com.feicheng.service.IFPayService;
import com.feicheng.wxpay.app.WxAppPayBean;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service("wx-pay-service")
public class FWxPayServiceImpl extends BaseService implements IFPayService {

    @Resource(name = "store-wx-pay")
    Map<String, WxAppPayBean> storeWxPayService;

    @Autowired
    IStoreClientService storeClientService;

    @Autowired
    IPaymentRecordService paymentRecordService;


    @Autowired
    IDepositRecordService depositRecordService;

    @Autowired
    IClientDepositService clientDepositService;
    
    @Autowired
    IDepositBackRequestService depositBackRequestService;

    @Autowired
    IConsumeRecordService consumeRecordService;

    @Autowired
    IOrderConsumeService orderConsumeService;

    @Autowired
    IVipService vipService;

    @Autowired
    IClientVipService clientVipService;

    @Autowired
    IOrderService orderService;

    @Autowired
    IAccountService accountService;

    @Resource(name = "wx-http")
    WxHTTPInterface wxHTTPInterface;

    @Autowired
    ICardTypeService cardTypeService;

    @Autowired
    ICardRecordService cardRecordService;

    @Override
    public ResultInfo pay(JSONObject param) {
        return null;
    }

    @Override
    public ResultInfo refundDeposit(Integer clientId, String openId, Integer storeId, Integer amount, Integer payId) {
        WxPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
        if (null == wxPayBean) {
            throw new SystemException(wxHTTPInterface.getPayNotReady());
        }

//        PaymentRecord paymentRecord = new PaymentRecord();
//        LocalDateTime current = LocalDateTime.now();
//        paymentRecord.setIoFlg(false); // 出账
//        paymentRecord.setPayType(Constant.VAL.PAY_TYPE.WX);
//        paymentRecord.setCt(current);
//        paymentRecordService.save(paymentRecord);
//
//        DepositRecord depositRecord = new DepositRecord();
//        depositRecord.setPayId(paymentRecord.getPayId());
//        depositRecord.setReason("押金退还");
//        depositRecord.setAmount(amount);
//        depositRecord.setType(Constant.VAL.DEPOSIT.OUT);
//        depositRecordService.save(depositRecord);
//
//        ClientDeposit clientDeposit = new ClientDeposit();
//        clientDeposit.setClientId(clientId);
//        clientDeposit.setDepositId(depositRecord.getDepositId());
//        clientDepositService.save(clientDeposit);

        // TODO 付款到零钱
//        String outTradeNo = wxPayBean.createOutTradeNoForDepositOut(storeId, clientId, payId);

        // TODO 转账失败
//        if ("".equals(outTradeNo)) {
//            throw new SystemException("转载失败");
//        }

        // TODO 转载成功会得到微信付款单号
        String paymentNo = "微信付款单号";

        return ResultInfo.success(paymentNo);
    }

    private ResultInfo preDepositPay(
            Integer clientId, Integer storeId,
            Integer amount, Integer payId,
            String openId,
            Integer depositId) {
        WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
        if (null == wxPayBean) {
            throw new SystemException(wxHTTPInterface.getPayNotReady());
        }

        PrepayWithRequestPaymentResponse preResult = wxPayBean.jsapi(
                wxPayBean.createOutTradeNoForDepositIn(
                        storeId, clientId, payId),
                wxHTTPInterface.getPayDepositInfo(),
                wxHTTPInterface.getNoticeDeposit() + storeId + "/"+depositId,
                amount,
                openId);

        try {
        	String prepayId = preResult.getPackageVal().split("=")[1];
        	
        	JSONObject result = new JSONObject();
            result.put("prepayId", prepayId);
            result.put("merchantId", wxPayBean.getMerchantId());
            result.put("tokenJSAPI", preResult);

            return ResultInfo.success(result);
        } catch (Exception e) {
        	log.error(wxHTTPInterface.getPayDepositInfo()+"下单失败：调用统一下单接口无法取得前置ID", e);
        	throw new SystemException(wxHTTPInterface.getPayDepositInfo()+"下单失败：调用统一下单接口无法取得前置ID");
        }
    }

    @Override
    @Transactional
    public ResultInfo depositNewPay(Integer clientId, Integer storeId, Integer amount, String openId) {
        PaymentRecord paymentRecordForDeposit = new PaymentRecord();
        paymentRecordForDeposit.setCt(LocalDateTime.now());
        paymentRecordForDeposit.setPayType(Constant.VAL.PAY_TYPE.WX);
        paymentRecordForDeposit.setPayFlg(false);
        paymentRecordService.save(paymentRecordForDeposit);

        DepositRecord depositRecord = new DepositRecord();
        depositRecord.setAmount(amount);
        depositRecord.setPayId(paymentRecordForDeposit.getPayId());
        depositRecord.setType(Constant.VAL.DEPOSIT.INS);
        depositRecord.setCt(LocalDateTime.now());
        depositRecord.setStoreId(storeId);
        depositRecord.setReason(wxHTTPInterface.getPayDepositInfo());
        depositRecordService.save(depositRecord);

        ClientDeposit clientDeposit = new ClientDeposit();
        clientDeposit.setClientId(clientId);
        clientDeposit.setDepositId(depositRecord.getDepositId());
        clientDeposit.setStatu("CRT");
        clientDepositService.save(clientDeposit);

        ResultInfo prePayResult = this.preDepositPay(
                clientId,
                storeId,
                amount,
                paymentRecordForDeposit.getPayId(),
                openId,
                depositRecord.getDepositId()
        );
        JSONObject prePayData =
                prePayResult.getObject(Constant.RESULT.DATA, JSONObject.class);
        paymentRecordForDeposit.setPrepayId(prePayData.getString("prepayId"));
        paymentRecordService.updateById(paymentRecordForDeposit);

        return ResultInfo.success(prePayData);
    }

    @Override
    @Transactional
    public ResultInfo depositExPay(Integer clientId, Integer storeId, Integer depositRecordId, String openId) {

        WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
        if (null == wxPayBean) {
            throw new SystemException(wxHTTPInterface.getPayNotReady());
        }

        DepositRecord depositRecord = depositRecordService.getById(depositRecordId);
        PaymentRecord paymentRecord = paymentRecordService.getById(depositRecord.getPayId());

        if (paymentRecord.getPayFlg()) {
            throw new SystemException("已经完成支付，无需重复支付。");
        }

        ResultInfo prePayResult = this.preDepositPay(
                clientId,
                storeId,
                depositRecord.getAmount(),
                paymentRecord.getPayId(),
                openId,
                depositRecordId
        );
        JSONObject prePayData =
                prePayResult.getObject(Constant.RESULT.DATA, JSONObject.class);
        paymentRecord.setPrepayId(prePayData.getString("prepayId"));
        paymentRecordService.updateById(paymentRecord);

        return ResultInfo.success(prePayData);
    }

    @Override
    @Transactional
    public ResultInfo depositNotice(
    		String nonce,
    		String signature,
    		String signatureType,
    		String timestamp,
    		String serial,
    		String body,
            Integer storeId,
            Integer depositId) {
    	
        try {
            WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
            if (null == wxPayBean) {
                throw new SystemException(wxHTTPInterface.getPayNotReady());
            }
            
            Transaction transaction = wxPayBean.parseNotice(nonce, signature, signatureType, timestamp, serial, body);

            if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                String outTradeNo = transaction.getOutTradeNo();
                Integer amount = transaction.getAmount().getTotal();

                JSONObject idsObject = wxPayBean.releaseOutTradeNo(outTradeNo);
                Integer clientId = idsObject.getInteger(Constant.ENTITY.CLIENT.CLIENT_ID);
                Integer payId = idsObject.getInteger(Constant.ENTITY.PAYMENT_RECORD.PAY_ID);

                PaymentRecord paymentRecord = new PaymentRecord();
                paymentRecord.setPayId(payId);
                paymentRecord.setOriginalCode(outTradeNo);
                paymentRecord.setPayFlg(true);
                paymentRecordService.updateById(paymentRecord);
                
                QueryWrapper<ClientDeposit> cdqr = new QueryWrapper<ClientDeposit>();
                cdqr.eq("deposit_id", depositId);
                cdqr.eq("client_id", clientId);
                List<ClientDeposit> cdList = this.clientDepositService.list(cdqr);
                
                ClientDeposit cd = cdList.get(0);
                cd.setStatu("PAY");
                this.clientDepositService.update(cd, cdqr);
                
                QueryWrapper<Account> accountQueryWrapper = new QueryWrapper<Account>();
                accountQueryWrapper.eq(Constant.ENTITY.ACCOUNT.CLIENT_ID, clientId);

                List<Account> accounts = accountService.list(accountQueryWrapper);
                if (0 < accounts.size()) {
                    Account account = accounts.get(0);
                    account.setDepositAmount(amount);
                    accountService.updateById(account);
                }
                
                DepositRecord dr = this.depositRecordService.getById(cd.getDepositId());
                dr.setStoreId(storeId);
                this.depositRecordService.updateById(dr);
                
            }
        } catch (Exception e) {
            log.error(wxHTTPInterface.getPayNoFinish(), e);
            return ResultInfo.fail(wxHTTPInterface.getPayFail());
        }

        return ResultInfo.success();
    }

    @Override
    @Transactional
    public ResultInfo orderPay(Integer orderId, Integer clientId, Integer storeId, Integer amount, String openId) {

    	Order order = this.orderService.getById(orderId);
    	
    	QueryWrapper<StoreClient> qwSc = new QueryWrapper<StoreClient>();
    	qwSc.eq("client_id", clientId);
    	qwSc.eq("store_id", order.getStoreId());
    	qwSc.eq("wx_type", "Y");
    	List<StoreClient> scList = this.storeClientService.list(qwSc);
    	
    	if (null != scList && !scList.isEmpty()) {
    		storeId = order.getStoreId();
    		openId = scList.remove(0).getOpenId();
    	}
    	
    	WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
        if (null == wxPayBean) {
            throw new SystemException(wxHTTPInterface.getPayNotReady());
        }

        PaymentRecord paymentRecordForOrder = new PaymentRecord();
        paymentRecordForOrder.setCt(LocalDateTime.now());
        paymentRecordForOrder.setPayType(Constant.VAL.PAY_TYPE.WX);
        paymentRecordForOrder.setPayFlg(false);
        paymentRecordService.save(paymentRecordForOrder);

        PrepayWithRequestPaymentResponse preResult = wxPayBean.jsapi(
                wxPayBean.createOutTradeNoForOrder(orderId, storeId, clientId, paymentRecordForOrder.getPayId()),
                wxHTTPInterface.getPayOrderInfo(),
                wxHTTPInterface.getNoticeOrder() + storeId,
                amount,
                openId);

        try {
        	String prepayId = preResult.getPackageVal().split("=")[1];
        	
        	paymentRecordForOrder.setPrepayId(prepayId);
            paymentRecordForOrder.setPayType(null);
            paymentRecordForOrder.setCt(null);
            paymentRecordService.updateById(paymentRecordForOrder);

            return ResultInfo.success(preResult);
        } catch (Exception e) {
        	log.error("下单失败：调用统一下单接口无法取得前置ID", e);
        	throw new SystemException("下单失败：调用统一下单接口无法取得前置ID");
        }

    }

    @Override
    @Transactional
    public ResultInfo orderNotice(
    		String nonce,
    		String signature,
    		String signatureType,
    		String timestamp,
    		String serial,
    		String body, 
    		Integer storeId) {
    	
        try {
            WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
            if (null == wxPayBean) {
                throw new SystemException(wxHTTPInterface.getPayNotReady());
            }

            Transaction transaction = wxPayBean.parseNotice(nonce, signature, signatureType, timestamp, serial, body);

            if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                String outTradeNo = transaction.getOutTradeNo();
                JSONObject idsObject = wxPayBean.releaseOutTradeNo(outTradeNo);
                Integer payId = idsObject.getInteger(Constant.ENTITY.PAYMENT_RECORD.PAY_ID);
                Integer orderId = idsObject.getInteger(Constant.ENTITY.ORDER.ORDER_ID);

                PaymentRecord paymentRecord = paymentRecordService.getById(payId);
                if (!StringUtils.hasLength(paymentRecord.getOriginalCode())) {
                    paymentRecord.setOriginalCode(outTradeNo);
                    paymentRecord.setPayFlg(true);
                    paymentRecordService.updateById(paymentRecord);

                    ConsumeRecord consumeRecord = new ConsumeRecord();
                    consumeRecord.setPayId(payId);
                    consumeRecord.setType(Constant.CONSUME_TYPE.P);
                    consumeRecordService.save(consumeRecord);

                    OrderConsume orderConsume = new OrderConsume();
                    orderConsume.setOrderId(orderId);
                    orderConsume.setConsumeRecordId(consumeRecord.getConsumeRecordId());
                    orderConsumeService.save(orderConsume);

                    Order order = new Order();
                    order.setStoreId(storeId);
                    order.setOrderId(orderId);
                    order.setStatu(Constant.MODEL_NAME.Order.FIN);
                    orderService.updateById(order);
                }
            }

        } catch (Exception e) {
            log.error(wxHTTPInterface.getPayNoFinish(), e);
            return ResultInfo.fail(wxHTTPInterface.getPayFail());
        }

        return ResultInfo.success();
    }

    @Override
    @Transactional
    public ResultInfo cardPay(
            Integer clientId,
            Integer storeId,
            Integer amount,
            String openId,
            Integer cardTypeId) {

        WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
        if (null == wxPayBean) {
            throw new SystemException(wxHTTPInterface.getPayNotReady());
        }


        PaymentRecord paymentRecordForDeposit = new PaymentRecord();
        paymentRecordForDeposit.setCt(LocalDateTime.now());
        paymentRecordForDeposit.setPayType(Constant.VAL.PAY_TYPE.WX);
        paymentRecordForDeposit.setPayFlg(false);
        paymentRecordService.save(paymentRecordForDeposit);

        PrepayWithRequestPaymentResponse preResult = wxPayBean.jsapi(
                wxPayBean.createOutTradeNoForCardIn(storeId, clientId, paymentRecordForDeposit.getPayId()),
                "会员卡",
                new StringBuilder(wxHTTPInterface.getNoticeCardRecord())
                        .append(storeId)
                        .append("/").append(paymentRecordForDeposit.getPayId())
                        .append("/").append(cardTypeId)
                        .toString(),
                amount,
                openId);
        try {
        	String prepayId = preResult.getPackageVal().split("=")[1];
        	paymentRecordForDeposit.setPrepayId(prepayId);
            paymentRecordForDeposit.setPayType(null);
            paymentRecordForDeposit.setCt(null);
            paymentRecordService.updateById(paymentRecordForDeposit);
            return ResultInfo.success(preResult);
        } catch (Exception e) {
        	log.error("下单失败：调用统一下单接口无法取得前置ID", e);
        	throw new SystemException("下单失败：调用统一下单接口无法取得前置ID");
        }
    }

    @Override
    @Transactional
    public ResultInfo cardNotice(
    		String nonce,
    		String signature,
    		String signatureType,
    		String timestamp,
    		String serial,
    		String body, 
    		Integer storeId, Integer payId, Integer cardTypeId) {
    	
        try {
            WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
            if (null == wxPayBean) {
                throw new SystemException(wxHTTPInterface.getPayNotReady());
            }

            Transaction transaction = wxPayBean.parseNotice(nonce, signature, signatureType, timestamp, serial, body);

            if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                String outTradeNo = transaction.getOutTradeNo();
                Integer amount = transaction.getAmount().getTotal();

                JSONObject idsObject = wxPayBean.releaseOutTradeNo(outTradeNo);
                Integer clientId = idsObject.getInteger(Constant.ENTITY.CLIENT.CLIENT_ID);

                CardType cardType = cardTypeService.getById(cardTypeId);
                PaymentRecord paymentRecord = paymentRecordService.getById(payId);
                if (!StringUtils.hasLength(paymentRecord.getOriginalCode())) {
                    paymentRecord.setOriginalCode(outTradeNo);
                    paymentRecord.setPayFlg(true);
                    paymentRecordService.updateById(paymentRecord);

                    CardRecord cardRecord = new CardRecord();
                    cardRecord.setAmount(amount);
                    cardRecord.setPayId(payId);
                    cardRecord.setCardTypeId(cardTypeId);
                    cardRecord.setTimeLongs(cardType.getTimeLongs());
                    cardRecord.setStatu(true);
                    cardRecord.setStoreId(storeId);
                    cardRecordService.save(cardRecord);


                    Vip vip = new Vip();
                    vip.setCardRecordId(cardRecord.getRecordId());
                    vip.setCt(LocalDateTime.now());
                    vip.setVipType(Constant.VIP_TYPE.C);
                    vip.setStatu(0);
                    vipService.save(vip);

                    ClientVip clientVip = new ClientVip();
                    clientVip.setClientId(clientId);
                    clientVip.setVipId(vip.getVipId());
                    clientVipService.save(clientVip);

                }
            }

        } catch (Exception e) {
            log.error(wxHTTPInterface.getPayNoFinish(), e);
            return ResultInfo.fail(wxHTTPInterface.getPayFail());
        }

        return ResultInfo.success();
    }

	@Override
	public ResultInfo refoundNotice(String nonce, String signature, String signatureType, String timestamp,
			String serial, String body, Integer storeId) {
		
		try {
            WxAppPayBean wxPayBean = storeWxPayService.get(String.valueOf(storeId));
            if (null == wxPayBean) {
                throw new SystemException(wxHTTPInterface.getPayNotReady());
            }

            RefundNotification transaction = wxPayBean.parseRefundNotice(nonce, signature, signatureType, timestamp, serial, body);
            String outRefundNo = transaction.getOutRefundNo();
            String[] arrIds = outRefundNo.split("REF");
            Integer backId = Integer.valueOf(arrIds[0]);
            Integer depositId = Integer.valueOf(arrIds[1]);
            
            
            QueryWrapper<ClientDeposit> clientDepositQueryWrapper = new QueryWrapper<ClientDeposit>();
            clientDepositQueryWrapper.eq("deposit_id", depositId);
            
            ClientDeposit clientDeposit = clientDepositService.getOne(clientDepositQueryWrapper);
            DepositBackRequest depositBackRequest = depositBackRequestService.getById(backId);
            
            if (Status.SUCCESS.equals(transaction.getRefundStatus())) {
            	
            	depositBackRequest.setStatu(Constant.VAL.BACK_REQUEST_DEPOSIT.BCK);
                depositBackRequest.setUt(LocalDateTime.now());
                depositBackRequestService.updateById(depositBackRequest);
                
                QueryWrapper<Account> acQw = new QueryWrapper<Account>();
        		acQw.eq("client_id", clientDeposit.getClientId());
        		Account account = this.accountService.getOne(acQw);
        		account.setDepositAmount(0);
                this.accountService.updateById(account);
                
                clientDeposit.setStatu(Constant.VAL.BACK_REQUEST_DEPOSIT.BCK);
            } else if (Status.CLOSED.equals(transaction.getRefundStatus())) {
            	clientDeposit.setStatu(Constant.VAL.BACK_REQUEST_DEPOSIT.CLO);
            } else if (Status.ABNORMAL.equals(transaction.getRefundStatus())) {
            	clientDeposit.setStatu(Constant.VAL.BACK_REQUEST_DEPOSIT.EXC);
            }
            
            clientDepositService.update(clientDeposit, clientDepositQueryWrapper);

        } catch (Exception e) {
            log.error("退款失败", e);
            return ResultInfo.fail(wxHTTPInterface.getPayFail());
        }

        return ResultInfo.success();
	}
}
